Related to:
- Fig 3c-h
- Extended Data Fig 6b,c
- Extended Data Fig 7f
suppressPackageStartupMessages({
library(Seurat)
library(SeuratObject)
library(SummarizedExperiment)
library(ggplot2)
library(ggpubr)
library(tidyr)
library(patchwork)
library(viridis)
})
SAVEFIGS <- FALSE
TBO_seurat<-readRDS("../data/05_2025_RPM_RPMA_TBO_CellTag_Seurat_wSigs_FA_dpt_final.rds")
TBO_seurat
An object of class Seurat
110982 features across 26618 samples within 2 assays
Active assay: RNA (55491 features, 0 variable features)
2 layers present: counts, data
1 other assay present: norm
2 dimensional reductions calculated: umap, fa
clones <- readRDS("../data/05_2025_RPM_RPMA_TBOAllo_CellTagClones_Onlyclones.rds")
my_colors <- c(
"#E41A1C", # strong red
"#377EB8", # medium blue
"#4DAF4A", # green
"#984EA3", # purple
"#FF7F00", # orange
"#FFFF33", # yellow
"#A65628", # brown
"#e7298a", # pink
"#666666", # grey
"#66C2A5", # teal
"#FC8D62", # salmon
"#8DA0CB", # soft blue
"#E78AC3", # soft pink (different from 8)
"#A6D854", # light green (but yellowish tint, not green)
"#FFD92F", # lemon yellow
"#E5C494", # light brown
"#B3B3B3", # light grey
"#1B9E77", # deep teal
"#D95F02", # dark orange
"#7570B3", # strong purple
"#66A61E" # olive green (NOT same green as before)
)
pheno_col <- c("brown2","darkorchid4","dodgerblue","#66A61E","orange","turquoise4","turquoise")
names(pheno_col) <- c("NE","NE/Neuronal","Neuronal","ATOH1","Tuft","Triple-Neg","Basal")
Fig 3c
ForceAtlas embedding of RPM and RPMA CellTagged allografted tumor
cells
DimPlot(TBO_seurat, group.by='leiden_scVI_1.2', cols=my_colors, reduction='fa', label=TRUE, label.size=4) &
NoAxes() + theme(legend.position="none") # suppress legend

DimPlot(TBO_seurat, group.by='Pheno', cols=pheno_col, reduction='fa', label=FALSE, label.size=6) &
NoAxes()

# Extract coordinates from force-directed layout (or any layout)
dat <- Embeddings(TBO_seurat, reduction = "fa") %>%
as.data.frame() %>%
mutate(Cluster = TBO_seurat$leiden_scVI_1.2,
Genotype = TBO_seurat$Genotype)
# Set up your color vector (named)
my_colors_named <- setNames(my_colors, levels(TBO_seurat$leiden_scVI_1.2))
# Plot each Genotype with background cells greyed
plots <- lapply(unique(dat$Genotype), function(g) {
dat$highlight <- ifelse(dat$Genotype == g, "highlight", "background")
dat$Color <- ifelse(dat$Genotype == g, my_colors_named[dat$Cluster], "lightgrey")
ggplot(dat, aes(x = FA_1, y = FA_2)) +
geom_point(aes(color = Color), size = 0.5) +
scale_color_identity() +
ggtitle(g) +
theme_void() +
theme(
plot.title = element_text(hjust = 0.5),
legend.position = "none",
plot.background = element_rect(fill = "transparent", color = NA)
)
})
# Combine into one figure
a <- wrap_plots(plots, ncol = 2)
a

if(SAVEFIGS) ggsave("FAbyGeno.png", plot= a, width=6, height=3, dpi=300, bg = "transparent")
Visualizing Celltag/clone data
From table above, added whether clones were robust or not
Robust defined as >5 cells per clone post-QC.
CellTag metadata: clone information can also be found in Supplementary
Table 4 of Ireland et al, 2025
Idents(TBO_seurat) <- 'Robust'
table(TBO_seurat@meta.data$Robust, TBO_seurat@meta.data$UnID)
RPMA_Allo RPM_Allo3 RPM_Allo4 RPM_Allo_New
No 6659 5244 6523 2227
Robust 3501 1474 337 653
table(TBO_seurat@meta.data$Robust, TBO_seurat@meta.data$GenoCT)
RPMA_CTpreCre RPM_CTpostCre RPM_CTpreCre
No 6659 11767 2227
Robust 3501 1811 653
Identify robust clones
table(clones@meta.data$Robust, clones@meta.data$Genotype)
RPM RPMA
Robust 2464 3501
Assess clones by leiden cluster
Fig. 3d
First, just look at bar graph, no particular order #
Idents(clones) <- 'leiden_scVI_1.2'
x <- table(clones@meta.data$CellTag_Clone,Idents(clones))
proportions <- as.data.frame(100*prop.table(x, margin = 1))
# proportions$Cluster
colnames(proportions)<-c("Cluster", "Sample", "Frequency")
# Stacked
p <- ggplot(proportions, aes(fill=Sample, y=Frequency, x=Cluster)) +
geom_bar(position="stack", stat="identity")
p + scale_fill_manual(values=my_colors) +
theme_bw()+ theme(axis.text.y = element_text(size=20),
axis.text.x=element_text(size=14), axis.title.x =element_text(size=14),
axis.title.y = element_text(size=18), legend.text = element_text(size=12),
legend.title = element_text(size=18))+rotate_x_text(size=7,angle = 90)

dat <- p$data
Transform data to perform hierarchical (agglomerative) clustering
# Pivot to wide format: Cluster × Sample
mat <- dat %>%
pivot_wider(names_from = Sample, values_from = Frequency, values_fill = 0) %>%
tibble::column_to_rownames("Cluster") %>%
as.matrix()
mat_norm <- prop.table(mat, margin = 1) # normalize each row to sum to 1
Hierarchical (agglomerative) clustering of clones with default
parameters in pheatmap()
hm <- pheatmap::pheatmap(t(mat), cutree_rows = 1, cutree_cols = 8, cellwidth = 5,
cellheight = 5, fontsize = 8,
cluster_rows=FALSE, border_color=NA,
color = colorRampPalette(c("darkturquoise","black","red2"))(30))

clone_order <- colnames(t(mat))[hm$tree_col$order]
Fig 3d
Clustered clones by proportions of cells in Leiden clusters
# Now, plot clones by Leiden, ordered, final # (Fig. 3d)
Idents(clones)<-'leiden_scVI_1.2'
x <- table(clones@meta.data$CellTag_Clone,Idents(clones))
proportions <- as.data.frame(100*prop.table(x, margin = 1))
colnames(proportions) <- c("Cluster", "Sample", "Frequency")
proportions$Cluster <- factor(proportions$Cluster, clone_order)
# Stacked
p <- ggplot(proportions, aes(fill=Sample, y=Frequency, x=Cluster)) +
geom_bar(position="stack", stat="identity")
p + scale_fill_manual(values=my_colors) +
theme_bw() + theme(axis.text.y = element_text(size=20),
axis.text.x=element_text(size=14), axis.title.x =element_text(size=14),
axis.title.y = element_text(size=18), legend.text = element_text(size=12),
legend.title = element_text(size=18))+rotate_x_text(size=7,angle = 90) +
labs(x = NULL, y = "% of clone")

Idents(clones) <- 'Clone_Dynamics'
table(clones@meta.data$Clone_Dynamics)
Pattern_1 Pattern_2 Pattern_3 Pattern_4 Pattern_5 Unknown_1 Unknown_2
553 675 693 2797 1032 167 48
Function for plotting clone dynamics
clone_patterns <- c('Pattern_1', 'Pattern_2', 'Pattern_3', 'Pattern_4', 'Pattern_5', 'Unknown_1', 'Unknown_2')
pattern_colors <- c('orange', 'green2', 'red', 'royalblue2', 'purple', 'gray40', 'black')
names(pattern_colors) <- clone_patterns
plotCloneDyn <- function(clone_pattern, pattern_color, tit = "") {
# Get cells of interest
ss <- subset(clones, idents = clone_pattern)
highlighted_cells <- rownames(ss@meta.data)
# Plot all cells in grey, highlight the pattern in your chosen color
DimPlot(clones,
reduction = "fa",
cells.highlight = highlighted_cells,
sizes.highlight = 0.25,
cols.highlight = pattern_color,
cols = "grey90",
pt.size = 0.1) +
ggtitle(tit) & NoLegend() & NoAxes()
}
Fig 3e
ForceAtlas embedding showing representative clones of each pattern
with cells colored by pattern
cdp <- (lapply(1:7, function(i) plotCloneDyn(clone_patterns[i], pattern_colors[i], tit=clone_patterns[i])))
p_combined <- ggarrange(plotlist = cdp, ncol = 4, nrow = 2)
p_combined

getCloneCellIDs <- function(id) {
cellIDs <- rownames(subset(clones,idents=c(id))@meta.data)
}
clone_cell_ids <- lapply(setNames(clone_patterns, clone_patterns), getCloneCellIDs)
plotPhenoWithBackground <- function(clone_pattern, pheno_col, title = "", seurat_obj = clones) {
# Check if 'fa' reduction exists
if (!"fa" %in% names(seurat_obj@reductions)) {
stop("The 'fa' reduction is not found in the Seurat object.")
}
# Extract FA coordinates and metadata
coords <- as.data.frame(Embeddings(seurat_obj, reduction = "fa"))
coords$cell <- rownames(coords)
# Get metadata
meta <- seurat_obj@meta.data[, c("Clone_Dynamics", "Pheno")]
meta$cell <- rownames(meta)
# Merge coordinates and metadata
dat <- merge(coords, meta, by = "cell")
# Flag highlighted cells
dat$highlight <- dat$Clone_Dynamics == clone_pattern
dat$Pheno <- droplevels(dat$Pheno)
# Split background and foreground
bg_dat <- dat[!dat$highlight, ]
fg_dat <- dat[dat$highlight, ]
if (nrow(fg_dat) == 0) {
warning(paste("No cells found for Clone_Dynamics pattern:", clone_pattern))
return(ggplot() + ggtitle(paste(clone_pattern, "(no cells)")))
}
# Plot
p <- ggplot() +
geom_point(data = bg_dat, aes(x = FA_1, y = FA_2), color = "gray90", size = 0.2) +
geom_point(data = fg_dat, aes(x = FA_1, y = FA_2, color = Pheno), size = 1) +
scale_color_manual(values = pheno_col, drop = FALSE) +
ggtitle(title) +
theme_void() +
theme(
legend.position = "none",
panel.background = element_rect(fill = "transparent", color = NA),
plot.background = element_rect(fill = "transparent", color = NA)
)
return(p)
}
ForceAtlas embedding showing cells colored by SCLC phenotype
DimPlot(clones, group.by='Pheno', cols=pheno_col, reduction='fa', label=FALSE, label.size=6, pt.size = 0.05) &
NoAxes()

Fig 3f
ForceAtlas embedding showing representative clones of each pattern
with cells colored by SCLC phenotype
plots <- lapply(clone_patterns, function(pat) plotPhenoWithBackground(pat, pheno_col, title = pat))
p_combined <- ggarrange(plotlist = plots, ncol = 4, nrow = 2)
p_combined

Visualize individual clones
Ext Data Fig. 6b,c
plotPatternDynByClone <- function(clone_pattern) {
Idents(clones)<-'Clone_Dynamics'
p1 <- subset(clones,idents=c(clone_pattern))
pattern_color <- pattern_colors[clone_pattern]
test <- as.data.frame(p1$CellTag_Clone)
test$Barcodes <- rownames(test)
# Group by CellTag_Clone
test <- test %>% group_by(p1$CellTag_Clone)
test <- dplyr::group_split(test)
n_clones <- length(test)
# Plot in for loop all RPM clones in Pattern 1
plot_lst <- vector("list", length = n_clones)
for (i in seq(n_clones)) {
g <- DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', order=TRUE,
cells.highlight=test[[i]]$Barcodes,sizes.highlight=2,
cols.highlight=pattern_color) +
ggtitle(paste0(test[[i]]$`p1$CellTag_Clone`[1])) +
theme(plot.title = element_text(size = 8,face = "plain")) & NoLegend() &
NoAxes()
plot_lst[[i]] <- g
}
# Combine multiple plots for output, as desired
return(cowplot::plot_grid(plotlist = plot_lst, ncol=5))
}
pattern_plot_list <- lapply(clone_patterns, plotPatternDynByClone)
n_plots_per_pattern <- sapply(pattern_plot_list, function(x) length(x[['layers']]))
## calculate height of figure based on number of plots per pattern
plt_height <- 2.667 * ((n_plots_per_pattern %/% 5) + 1)
Ext Data Fig 6b,c
pattern_plot_list[[1]]

pattern_plot_list[[2]]

pattern_plot_list[[3]]

pattern_plot_list[[4]]

pattern_plot_list[[5]]

pattern_plot_list[[6]]

pattern_plot_list[[7]]

Visualize only clones matching in vivo in FA projection
Ext Data Fig. 7f
Idents(clones) <- "CellTag_Clone"
# Subset just the clones that match the in vitro, pattern 1
invitromatch <- subset(clones, idents=c("RPM_Clone_14","RPM_Clone_2","RPM_Clone_33","RPM_Clone_36","RPM_Clone_6"))
p1_cells <- colnames(invitromatch)
p2_cells <- colnames(subset(clones, idents="RPM_Clone_23"))
p5_cells <- colnames(subset(clones, idents="RPM_Clone_13"))
DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa',
order=TRUE, cells.highlight=p1_cells, sizes.highlight=2,
cols.highlight=c("orange")) + ggtitle("") & NoLegend() & NoAxes()

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa',
order=TRUE, cells.highlight=p2_cells, sizes.highlight=2,
cols.highlight=pattern_colors["Pattern_2"]) + ggtitle("") & NoLegend() & NoAxes()

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa',
order=TRUE, cells.highlight=p5_cells, sizes.highlight=2,
cols.highlight=pattern_colors["Pattern_5"]) + ggtitle("") & NoLegend() & NoAxes()

NA
NA
Fig. 3g
dpt psuedotime in FA space
FeaturePlot(TBO_seurat, features = c("dpt_pseudotime"), pt.size=0.01,
reduction='fa',) + scale_color_viridis(option="viridis",direction=-1)& NoAxes()

Function to plot clone dynamics colored by DPT
plotCloneDynDpt <- function(clone_pattern, seurat_obj, title = "") {
# Subset cells of interest
ss <- subset(seurat_obj, idents = clone_pattern)
highlight_cells <- colnames(ss)
# Extract embeddings and metadata
fa_coords <- Embeddings(seurat_obj, "fa")
dpt_vals <- seurat_obj@meta.data$dpt_pseudotime
names(dpt_vals) <- rownames(seurat_obj@meta.data)
dat <- as.data.frame(fa_coords)
dat$highlight <- ifelse(rownames(dat) %in% highlight_cells, "yes", "no")
dat$pseudotime <- dpt_vals[rownames(dat)]
colnames(dat)[1:2] <- c("FA1", "FA2") # name columns for clarity
library(ggplot2)
ggplot(dat, aes(x = FA1, y = FA2)) +
# Background cells
geom_point(data = subset(dat, highlight == "no"),
color = "grey85", size = 0.1) +
# Highlighted cells colored by pseudotime
geom_point(data = subset(dat, highlight == "yes"),
aes(color = pseudotime), size = 0.25) +
scale_color_viridis_c(option = "turbo", direction = -1) +
ggtitle(title) +
theme_void() +
theme(
legend.position = "none",
panel.border = element_blank(), # removes any panel border
plot.background = element_rect(fill = "transparent", color = NA),
panel.background = element_rect(fill = "transparent", color = NA),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
)
}
Fig 3h
Idents(clones) <- 'Clone_Dynamics'
my_pats <- c('Pattern_1','Pattern_2','Pattern_5','Unknown_1','Pattern_3','Pattern_4')
plots <- lapply(my_pats, function(pat) {
tit <- gsub(pat, '_', ' ')
plotCloneDynDpt(pat, clones, title = tit)
})
cowplot::plot_grid(plotlist = plots, ncol=6)

LS0tCnRpdGxlOiAiUlBNX1JQTUFfQWxsb3NfQ2VsbFRhZyBub3RlYm9vayIKYXV0aG9yOiAiQWJiaWUgSXJlbGFuZCwgRGFycmVuIFR5c29uIgpkYXRlOiAiMjAyNS0wNi0yNCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIE1vZGlmeWluZyBvcmlnaW5hbCBjb2RlCk9yaWdpbmFsIGZpbGU6IFtGaWczXzRfNl9FeHRGaWc1LTYtMTBfUlBNX1JQTUFfQWxsb3NfQ2VsbFRhZy5SXShQcmVwcm9jZXNzaW5nX2Zvcl9yZWZlcmVuY2UvUl9Db2RlL0ZpZzNfNF82X0V4dEZpZzUtNi0xMF9SUE1fUlBNQV9BbGxvc19DZWxsVGFnLlIpICAKCk9ubHkgQ2VsbFRhZyBhbmFseXNpcyBpbmNsdWRlZCBpbiB0aGlzIG5vdGVib29rLgoKIyMjIFJlbGF0ZWQgdG86CiogRmlnIDNjLWgKKiBFeHRlbmRlZCBEYXRhIEZpZyA2YixjCiogRXh0ZW5kZWQgRGF0YSBGaWcgN2YKCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogICAgbGlicmFyeShTZXVyYXQpCiAgICBsaWJyYXJ5KFNldXJhdE9iamVjdCkKICAgIGxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBsaWJyYXJ5KGdncHVicikKICAgIGxpYnJhcnkodGlkeXIpCiAgICBsaWJyYXJ5KHBhdGNod29yaykKICAgIGxpYnJhcnkodmlyaWRpcykKfSkKYGBgCmBgYHtyfQpTQVZFRklHUyA8LSBGQUxTRQpgYGAKCgpgYGB7cn0KVEJPX3NldXJhdDwtcmVhZFJEUygiLi4vZGF0YS8wNV8yMDI1X1JQTV9SUE1BX1RCT19DZWxsVGFnX1NldXJhdF93U2lnc19GQV9kcHRfZmluYWwucmRzIikKVEJPX3NldXJhdApgYGAKCmBgYHtyfQpjbG9uZXMgPC0gcmVhZFJEUygiLi4vZGF0YS8wNV8yMDI1X1JQTV9SUE1BX1RCT0FsbG9fQ2VsbFRhZ0Nsb25lc19Pbmx5Y2xvbmVzLnJkcyIpCmBgYAoKYGBge3J9Cm15X2NvbG9ycyA8LSBjKAogICIjRTQxQTFDIiwgIyBzdHJvbmcgcmVkCiAgIiMzNzdFQjgiLCAjIG1lZGl1bSBibHVlCiAgIiM0REFGNEEiLCAjIGdyZWVuCiAgIiM5ODRFQTMiLCAjIHB1cnBsZQogICIjRkY3RjAwIiwgIyBvcmFuZ2UKICAiI0ZGRkYzMyIsICMgeWVsbG93CiAgIiNBNjU2MjgiLCAjIGJyb3duCiAgIiNlNzI5OGEiLCAjIHBpbmsKICAiIzY2NjY2NiIsICMgZ3JleQogICIjNjZDMkE1IiwgIyB0ZWFsCiAgIiNGQzhENjIiLCAjIHNhbG1vbgogICIjOERBMENCIiwgIyBzb2Z0IGJsdWUKICAiI0U3OEFDMyIsICMgc29mdCBwaW5rIChkaWZmZXJlbnQgZnJvbSA4KQogICIjQTZEODU0IiwgIyBsaWdodCBncmVlbiAoYnV0IHllbGxvd2lzaCB0aW50LCBub3QgZ3JlZW4pCiAgIiNGRkQ5MkYiLCAjIGxlbW9uIHllbGxvdwogICIjRTVDNDk0IiwgIyBsaWdodCBicm93bgogICIjQjNCM0IzIiwgIyBsaWdodCBncmV5CiAgIiMxQjlFNzciLCAjIGRlZXAgdGVhbAogICIjRDk1RjAyIiwgIyBkYXJrIG9yYW5nZQogICIjNzU3MEIzIiwgIyBzdHJvbmcgcHVycGxlCiAgIiM2NkE2MUUiICAjIG9saXZlIGdyZWVuIChOT1Qgc2FtZSBncmVlbiBhcyBiZWZvcmUpCikKCnBoZW5vX2NvbCA8LSBjKCJicm93bjIiLCJkYXJrb3JjaGlkNCIsImRvZGdlcmJsdWUiLCIjNjZBNjFFIiwib3JhbmdlIiwidHVycXVvaXNlNCIsInR1cnF1b2lzZSIpCm5hbWVzKHBoZW5vX2NvbCkgPC0gYygiTkUiLCJORS9OZXVyb25hbCIsIk5ldXJvbmFsIiwiQVRPSDEiLCJUdWZ0IiwiVHJpcGxlLU5lZyIsIkJhc2FsIikKYGBgCgojIyMjIEZpZyAzYwpGb3JjZUF0bGFzIGVtYmVkZGluZyBvZiBSUE0gYW5kIFJQTUEgQ2VsbFRhZ2dlZCBhbGxvZ3JhZnRlZCB0dW1vciBjZWxscwpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSdsZWlkZW5fc2NWSV8xLjInLCBjb2xzPW15X2NvbG9ycywgcmVkdWN0aW9uPSdmYScsIGxhYmVsPVRSVUUsIGxhYmVsLnNpemU9NCkgJiAKICAgIE5vQXhlcygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgIyBzdXBwcmVzcyBsZWdlbmQKCmBgYAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NC41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSdQaGVubycsIGNvbHM9cGhlbm9fY29sLCByZWR1Y3Rpb249J2ZhJywgbGFiZWw9RkFMU0UsIGxhYmVsLnNpemU9NikgJiAKICAgIE5vQXhlcygpCmBgYAoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBFeHRyYWN0IGNvb3JkaW5hdGVzIGZyb20gZm9yY2UtZGlyZWN0ZWQgbGF5b3V0IChvciBhbnkgbGF5b3V0KQpkYXQgPC0gRW1iZWRkaW5ncyhUQk9fc2V1cmF0LCByZWR1Y3Rpb24gPSAiZmEiKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIG11dGF0ZShDbHVzdGVyID0gVEJPX3NldXJhdCRsZWlkZW5fc2NWSV8xLjIsCiAgICAgICAgIEdlbm90eXBlID0gVEJPX3NldXJhdCRHZW5vdHlwZSkKCiMgU2V0IHVwIHlvdXIgY29sb3IgdmVjdG9yIChuYW1lZCkKbXlfY29sb3JzX25hbWVkIDwtIHNldE5hbWVzKG15X2NvbG9ycywgbGV2ZWxzKFRCT19zZXVyYXQkbGVpZGVuX3NjVklfMS4yKSkKCiMgUGxvdCBlYWNoIEdlbm90eXBlIHdpdGggYmFja2dyb3VuZCBjZWxscyBncmV5ZWQKcGxvdHMgPC0gbGFwcGx5KHVuaXF1ZShkYXQkR2Vub3R5cGUpLCBmdW5jdGlvbihnKSB7CiAgZGF0JGhpZ2hsaWdodCA8LSBpZmVsc2UoZGF0JEdlbm90eXBlID09IGcsICJoaWdobGlnaHQiLCAiYmFja2dyb3VuZCIpCiAgZGF0JENvbG9yIDwtIGlmZWxzZShkYXQkR2Vub3R5cGUgPT0gZywgbXlfY29sb3JzX25hbWVkW2RhdCRDbHVzdGVyXSwgImxpZ2h0Z3JleSIpCgogIGdncGxvdChkYXQsIGFlcyh4ID0gRkFfMSwgeSA9IEZBXzIpKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IENvbG9yKSwgc2l6ZSA9IDAuNSkgKwogICAgc2NhbGVfY29sb3JfaWRlbnRpdHkoKSArCiAgICBnZ3RpdGxlKGcpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKQogICAgKQp9KQoKIyBDb21iaW5lIGludG8gb25lIGZpZ3VyZQphIDwtIHdyYXBfcGxvdHMocGxvdHMsIG5jb2wgPSAyKQphCgppZihTQVZFRklHUykgZ2dzYXZlKCJGQWJ5R2Vuby5wbmciLCBwbG90PSBhLCB3aWR0aD02LCBoZWlnaHQ9MywgZHBpPTMwMCwgYmcgPSAidHJhbnNwYXJlbnQiKQpgYGAKCgoKIyMjIFZpc3VhbGl6aW5nIENlbGx0YWcvY2xvbmUgZGF0YQoKRnJvbSB0YWJsZSBhYm92ZSwgYWRkZWQgd2hldGhlciBjbG9uZXMgd2VyZSByb2J1c3Qgb3Igbm90ICAKUm9idXN0IGRlZmluZWQgYXMgPjUgY2VsbHMgcGVyIGNsb25lIHBvc3QtUUMuICAKQ2VsbFRhZyBtZXRhZGF0YTogY2xvbmUgaW5mb3JtYXRpb24gY2FuIGFsc28gYmUgZm91bmQgaW4gU3VwcGxlbWVudGFyeSBUYWJsZSA0IG9mIElyZWxhbmQgZXQgYWwsIDIwMjUgIApgYGB7cn0KSWRlbnRzKFRCT19zZXVyYXQpIDwtICdSb2J1c3QnCmBgYAoKCmBgYHtyfQp0YWJsZShUQk9fc2V1cmF0QG1ldGEuZGF0YSRSb2J1c3QsIFRCT19zZXVyYXRAbWV0YS5kYXRhJFVuSUQpCnRhYmxlKFRCT19zZXVyYXRAbWV0YS5kYXRhJFJvYnVzdCwgVEJPX3NldXJhdEBtZXRhLmRhdGEkR2Vub0NUKQpgYGAKIyMjIElkZW50aWZ5IHJvYnVzdCBjbG9uZXMKYGBge3J9CnRhYmxlKGNsb25lc0BtZXRhLmRhdGEkUm9idXN0LCBjbG9uZXNAbWV0YS5kYXRhJEdlbm90eXBlKQpgYGAKCiMjIyBBc3Nlc3MgY2xvbmVzIGJ5IGxlaWRlbiBjbHVzdGVyCiMjIyMgRmlnLiAzZCAgCkZpcnN0LCBqdXN0IGxvb2sgYXQgYmFyIGdyYXBoLCBubyBwYXJ0aWN1bGFyIG9yZGVyICMKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTEwfQpJZGVudHMoY2xvbmVzKSA8LSAnbGVpZGVuX3NjVklfMS4yJwoKeCA8LSB0YWJsZShjbG9uZXNAbWV0YS5kYXRhJENlbGxUYWdfQ2xvbmUsSWRlbnRzKGNsb25lcykpCnByb3BvcnRpb25zIDwtIGFzLmRhdGEuZnJhbWUoMTAwKnByb3AudGFibGUoeCwgbWFyZ2luID0gMSkpCgojIHByb3BvcnRpb25zJENsdXN0ZXIKY29sbmFtZXMocHJvcG9ydGlvbnMpPC1jKCJDbHVzdGVyIiwgIlNhbXBsZSIsICJGcmVxdWVuY3kiKQoKIyBTdGFja2VkCnAgPC0gZ2dwbG90KHByb3BvcnRpb25zLCBhZXMoZmlsbD1TYW1wbGUsIHk9RnJlcXVlbmN5LCB4PUNsdXN0ZXIpKSArIAogIGdlb21fYmFyKHBvc2l0aW9uPSJzdGFjayIsIHN0YXQ9ImlkZW50aXR5IikKCnAgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXlfY29sb3JzKSArIAogIHRoZW1lX2J3KCkrIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAKICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksIGF4aXMudGl0bGUueCA9ZWxlbWVudF90ZXh0KHNpemU9MTQpLCAKICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCksIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLCAKICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCkpK3JvdGF0ZV94X3RleHQoc2l6ZT03LGFuZ2xlID0gOTApCgpkYXQgPC0gcCRkYXRhCmBgYApUcmFuc2Zvcm0gZGF0YSB0byBwZXJmb3JtIGhpZXJhcmNoaWNhbCAoYWdnbG9tZXJhdGl2ZSkgY2x1c3RlcmluZwpgYGB7cn0KIyBQaXZvdCB0byB3aWRlIGZvcm1hdDogQ2x1c3RlciDDlyBTYW1wbGUKbWF0IDwtIGRhdCAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gU2FtcGxlLCB2YWx1ZXNfZnJvbSA9IEZyZXF1ZW5jeSwgdmFsdWVzX2ZpbGwgPSAwKSAlPiUKICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiQ2x1c3RlciIpICU+JQogIGFzLm1hdHJpeCgpCgptYXRfbm9ybSA8LSBwcm9wLnRhYmxlKG1hdCwgbWFyZ2luID0gMSkgICMgbm9ybWFsaXplIGVhY2ggcm93IHRvIHN1bSB0byAxCmBgYAoKSGllcmFyY2hpY2FsIChhZ2dsb21lcmF0aXZlKSBjbHVzdGVyaW5nIG9mIGNsb25lcyB3aXRoIGRlZmF1bHQgcGFyYW1ldGVycyBpbiBgcGhlYXRtYXAoKWAKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0KaG0gPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHQobWF0KSwgY3V0cmVlX3Jvd3MgPSAxLCBjdXRyZWVfY29scyA9IDgsIGNlbGx3aWR0aCA9IDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgY2VsbGhlaWdodCA9IDUsIGZvbnRzaXplID0gOCwKICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cz1GQUxTRSwgYm9yZGVyX2NvbG9yPU5BLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJkYXJrdHVycXVvaXNlIiwiYmxhY2siLCJyZWQyIikpKDMwKSkKCmNsb25lX29yZGVyIDwtIGNvbG5hbWVzKHQobWF0KSlbaG0kdHJlZV9jb2wkb3JkZXJdCmBgYAoKCiMjIyMgRmlnIDNkCkNsdXN0ZXJlZCBjbG9uZXMgYnkgcHJvcG9ydGlvbnMgb2YgY2VsbHMgaW4gTGVpZGVuIGNsdXN0ZXJzCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwfQojIE5vdywgcGxvdCBjbG9uZXMgYnkgTGVpZGVuLCBvcmRlcmVkLCBmaW5hbCAjIChGaWcuIDNkKQpJZGVudHMoY2xvbmVzKTwtJ2xlaWRlbl9zY1ZJXzEuMicKCnggPC0gdGFibGUoY2xvbmVzQG1ldGEuZGF0YSRDZWxsVGFnX0Nsb25lLElkZW50cyhjbG9uZXMpKQpwcm9wb3J0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKDEwMCpwcm9wLnRhYmxlKHgsIG1hcmdpbiA9IDEpKQoKY29sbmFtZXMocHJvcG9ydGlvbnMpIDwtIGMoIkNsdXN0ZXIiLCAiU2FtcGxlIiwgIkZyZXF1ZW5jeSIpCnByb3BvcnRpb25zJENsdXN0ZXIgPC0gZmFjdG9yKHByb3BvcnRpb25zJENsdXN0ZXIsIGNsb25lX29yZGVyKQoKIyBTdGFja2VkCnAgPC0gZ2dwbG90KHByb3BvcnRpb25zLCBhZXMoZmlsbD1TYW1wbGUsIHk9RnJlcXVlbmN5LCB4PUNsdXN0ZXIpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb249InN0YWNrIiwgc3RhdD0iaWRlbnRpdHkiKQoKcCArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teV9jb2xvcnMpICsgCiAgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAKICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTQpLCBheGlzLnRpdGxlLnggPWVsZW1lbnRfdGV4dChzaXplPTE0KSwgCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE4KSwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCkpK3JvdGF0ZV94X3RleHQoc2l6ZT03LGFuZ2xlID0gOTApICsgCiAgICBsYWJzKHggPSBOVUxMLCB5ID0gIiUgb2YgY2xvbmUiKQoKYGBgCgpgYGB7cn0KSWRlbnRzKGNsb25lcykgPC0gJ0Nsb25lX0R5bmFtaWNzJwp0YWJsZShjbG9uZXNAbWV0YS5kYXRhJENsb25lX0R5bmFtaWNzKQpgYGAKCiMjIyBGdW5jdGlvbiBmb3IgcGxvdHRpbmcgY2xvbmUgZHluYW1pY3MKCmBgYHtyfQpjbG9uZV9wYXR0ZXJucyA8LSBjKCdQYXR0ZXJuXzEnLCAnUGF0dGVybl8yJywgJ1BhdHRlcm5fMycsICdQYXR0ZXJuXzQnLCAnUGF0dGVybl81JywgJ1Vua25vd25fMScsICdVbmtub3duXzInKQpwYXR0ZXJuX2NvbG9ycyA8LSBjKCdvcmFuZ2UnLCAnZ3JlZW4yJywgJ3JlZCcsICdyb3lhbGJsdWUyJywgJ3B1cnBsZScsICdncmF5NDAnLCAnYmxhY2snKQpuYW1lcyhwYXR0ZXJuX2NvbG9ycykgPC0gY2xvbmVfcGF0dGVybnMKYGBgCgpgYGB7cn0KcGxvdENsb25lRHluIDwtIGZ1bmN0aW9uKGNsb25lX3BhdHRlcm4sIHBhdHRlcm5fY29sb3IsIHRpdCA9ICIiKSB7CiAgICAjIEdldCBjZWxscyBvZiBpbnRlcmVzdAogICAgc3MgPC0gc3Vic2V0KGNsb25lcywgaWRlbnRzID0gY2xvbmVfcGF0dGVybikKICAgIGhpZ2hsaWdodGVkX2NlbGxzIDwtIHJvd25hbWVzKHNzQG1ldGEuZGF0YSkKICAgIAogICAgIyBQbG90IGFsbCBjZWxscyBpbiBncmV5LCBoaWdobGlnaHQgdGhlIHBhdHRlcm4gaW4geW91ciBjaG9zZW4gY29sb3IKICAgIERpbVBsb3QoY2xvbmVzLCAKICAgICAgICAgICAgcmVkdWN0aW9uID0gImZhIiwgCiAgICAgICAgICAgIGNlbGxzLmhpZ2hsaWdodCA9IGhpZ2hsaWdodGVkX2NlbGxzLAogICAgICAgICAgICBzaXplcy5oaWdobGlnaHQgPSAwLjI1LAogICAgICAgICAgICBjb2xzLmhpZ2hsaWdodCA9IHBhdHRlcm5fY29sb3IsCiAgICAgICAgICAgIGNvbHMgPSAiZ3JleTkwIiwgCiAgICAgICAgICAgIHB0LnNpemUgPSAwLjEpICsgCiAgICBnZ3RpdGxlKHRpdCkgJiBOb0xlZ2VuZCgpICYgTm9BeGVzKCkKfQpgYGAKCgojIyMjIEZpZyAzZQpGb3JjZUF0bGFzIGVtYmVkZGluZyBzaG93aW5nIHJlcHJlc2VudGF0aXZlIGNsb25lcyBvZiBlYWNoIHBhdHRlcm4gd2l0aCBjZWxscyBjb2xvcmVkIGJ5IHBhdHRlcm4KYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KY2RwIDwtIChsYXBwbHkoMTo3LCBmdW5jdGlvbihpKSBwbG90Q2xvbmVEeW4oY2xvbmVfcGF0dGVybnNbaV0sIHBhdHRlcm5fY29sb3JzW2ldLCB0aXQ9Y2xvbmVfcGF0dGVybnNbaV0pKSkKcF9jb21iaW5lZCA8LSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBjZHAsIG5jb2wgPSA0LCBucm93ID0gMikKcF9jb21iaW5lZApgYGAKCmBgYHtyfQpnZXRDbG9uZUNlbGxJRHMgPC0gZnVuY3Rpb24oaWQpIHsKICAgIGNlbGxJRHMgPC0gcm93bmFtZXMoc3Vic2V0KGNsb25lcyxpZGVudHM9YyhpZCkpQG1ldGEuZGF0YSkKfQpjbG9uZV9jZWxsX2lkcyA8LSBsYXBwbHkoc2V0TmFtZXMoY2xvbmVfcGF0dGVybnMsIGNsb25lX3BhdHRlcm5zKSwgZ2V0Q2xvbmVDZWxsSURzKQpgYGAKCgpgYGB7cn0KcGxvdFBoZW5vV2l0aEJhY2tncm91bmQgPC0gZnVuY3Rpb24oY2xvbmVfcGF0dGVybiwgcGhlbm9fY29sLCB0aXRsZSA9ICIiLCBzZXVyYXRfb2JqID0gY2xvbmVzKSB7CiAgICAjIENoZWNrIGlmICdmYScgcmVkdWN0aW9uIGV4aXN0cwogICAgaWYgKCEiZmEiICVpbiUgbmFtZXMoc2V1cmF0X29iakByZWR1Y3Rpb25zKSkgewogICAgICAgIHN0b3AoIlRoZSAnZmEnIHJlZHVjdGlvbiBpcyBub3QgZm91bmQgaW4gdGhlIFNldXJhdCBvYmplY3QuIikKICAgIH0KCiAgICAjIEV4dHJhY3QgRkEgY29vcmRpbmF0ZXMgYW5kIG1ldGFkYXRhCiAgICBjb29yZHMgPC0gYXMuZGF0YS5mcmFtZShFbWJlZGRpbmdzKHNldXJhdF9vYmosIHJlZHVjdGlvbiA9ICJmYSIpKQogICAgY29vcmRzJGNlbGwgPC0gcm93bmFtZXMoY29vcmRzKQoKICAgICMgR2V0IG1ldGFkYXRhCiAgICBtZXRhIDwtIHNldXJhdF9vYmpAbWV0YS5kYXRhWywgYygiQ2xvbmVfRHluYW1pY3MiLCAiUGhlbm8iKV0KICAgIG1ldGEkY2VsbCA8LSByb3duYW1lcyhtZXRhKQoKICAgICMgTWVyZ2UgY29vcmRpbmF0ZXMgYW5kIG1ldGFkYXRhCiAgICBkYXQgPC0gbWVyZ2UoY29vcmRzLCBtZXRhLCBieSA9ICJjZWxsIikKCiAgICAjIEZsYWcgaGlnaGxpZ2h0ZWQgY2VsbHMKICAgIGRhdCRoaWdobGlnaHQgPC0gZGF0JENsb25lX0R5bmFtaWNzID09IGNsb25lX3BhdHRlcm4KICAgIGRhdCRQaGVubyA8LSBkcm9wbGV2ZWxzKGRhdCRQaGVubykKCiAgICAjIFNwbGl0IGJhY2tncm91bmQgYW5kIGZvcmVncm91bmQKICAgIGJnX2RhdCA8LSBkYXRbIWRhdCRoaWdobGlnaHQsIF0KICAgIGZnX2RhdCA8LSBkYXRbZGF0JGhpZ2hsaWdodCwgXQoKICAgIGlmIChucm93KGZnX2RhdCkgPT0gMCkgewogICAgICAgIHdhcm5pbmcocGFzdGUoIk5vIGNlbGxzIGZvdW5kIGZvciBDbG9uZV9EeW5hbWljcyBwYXR0ZXJuOiIsIGNsb25lX3BhdHRlcm4pKQogICAgICAgIHJldHVybihnZ3Bsb3QoKSArIGdndGl0bGUocGFzdGUoY2xvbmVfcGF0dGVybiwgIihubyBjZWxscykiKSkpCiAgICB9CgogICAgIyBQbG90CiAgICBwIDwtIGdncGxvdCgpICsKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSBiZ19kYXQsIGFlcyh4ID0gRkFfMSwgeSA9IEZBXzIpLCBjb2xvciA9ICJncmF5OTAiLCBzaXplID0gMC4yKSArCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gZmdfZGF0LCBhZXMoeCA9IEZBXzEsIHkgPSBGQV8yLCBjb2xvciA9IFBoZW5vKSwgc2l6ZSA9IDEpICsKICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGhlbm9fY29sLCBkcm9wID0gRkFMU0UpICsKICAgICAgICBnZ3RpdGxlKHRpdGxlKSArCiAgICAgICAgdGhlbWVfdm9pZCgpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKSwKICAgICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKQogICAgICAgICkKCiAgICByZXR1cm4ocCkKfQpgYGAKCkZvcmNlQXRsYXMgZW1iZWRkaW5nIHNob3dpbmcgY2VsbHMgY29sb3JlZCBieSBTQ0xDIHBoZW5vdHlwZQpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00LjV9CkRpbVBsb3QoY2xvbmVzLCBncm91cC5ieT0nUGhlbm8nLCBjb2xzPXBoZW5vX2NvbCwgcmVkdWN0aW9uPSdmYScsIGxhYmVsPUZBTFNFLCBsYWJlbC5zaXplPTYsIHB0LnNpemUgPSAwLjA1KSAmIAogICAgTm9BeGVzKCkKYGBgCgoKIyMjIyBGaWcgM2YKRm9yY2VBdGxhcyBlbWJlZGRpbmcgc2hvd2luZyByZXByZXNlbnRhdGl2ZSBjbG9uZXMgb2YgZWFjaCBwYXR0ZXJuIHdpdGggY2VsbHMgY29sb3JlZCBieSBTQ0xDIHBoZW5vdHlwZQpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwbG90cyA8LSBsYXBwbHkoY2xvbmVfcGF0dGVybnMsIGZ1bmN0aW9uKHBhdCkgcGxvdFBoZW5vV2l0aEJhY2tncm91bmQocGF0LCBwaGVub19jb2wsIHRpdGxlID0gcGF0KSkKcF9jb21iaW5lZCA8LSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBwbG90cywgbmNvbCA9IDQsIG5yb3cgPSAyKQpwX2NvbWJpbmVkCmBgYAoKCiMjIyBWaXN1YWxpemUgaW5kaXZpZHVhbCBjbG9uZXMKRXh0IERhdGEgRmlnLiA2YixjCmBgYHtyfQpwbG90UGF0dGVybkR5bkJ5Q2xvbmUgPC0gZnVuY3Rpb24oY2xvbmVfcGF0dGVybikgewogICAgSWRlbnRzKGNsb25lcyk8LSdDbG9uZV9EeW5hbWljcycKICAgIHAxIDwtIHN1YnNldChjbG9uZXMsaWRlbnRzPWMoY2xvbmVfcGF0dGVybikpCiAgICAKICAgIHBhdHRlcm5fY29sb3IgPC0gcGF0dGVybl9jb2xvcnNbY2xvbmVfcGF0dGVybl0KCiAgICB0ZXN0IDwtIGFzLmRhdGEuZnJhbWUocDEkQ2VsbFRhZ19DbG9uZSkKICAgIHRlc3QkQmFyY29kZXMgPC0gcm93bmFtZXModGVzdCkKICAgICMgR3JvdXAgYnkgQ2VsbFRhZ19DbG9uZQogICAgdGVzdCA8LSB0ZXN0ICU+JSBncm91cF9ieShwMSRDZWxsVGFnX0Nsb25lKQogICAgdGVzdCA8LSBkcGx5cjo6Z3JvdXBfc3BsaXQodGVzdCkKICAgIAogICAgbl9jbG9uZXMgPC0gbGVuZ3RoKHRlc3QpCiAgICAjIFBsb3QgaW4gZm9yIGxvb3AgYWxsIFJQTSBjbG9uZXMgaW4gUGF0dGVybiAxCiAgICBwbG90X2xzdCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBuX2Nsb25lcykKICAgIGZvciAoaSBpbiBzZXEobl9jbG9uZXMpKSB7CiAgICAgIGcgPC0gRGltUGxvdChUQk9fc2V1cmF0LCBncm91cC5ieT0iQ2VsbFRhZ19DbG9uZSIsIHJlZHVjdGlvbj0nZmEnLCBvcmRlcj1UUlVFLCAKICAgICAgICAgICAgICAgICAgIGNlbGxzLmhpZ2hsaWdodD10ZXN0W1tpXV0kQmFyY29kZXMsc2l6ZXMuaGlnaGxpZ2h0PTIsIAogICAgICAgICAgICAgICAgICAgY29scy5oaWdobGlnaHQ9cGF0dGVybl9jb2xvcikgKwogICAgICAgICAgZ2d0aXRsZShwYXN0ZTAodGVzdFtbaV1dJGBwMSRDZWxsVGFnX0Nsb25lYFsxXSkpICsgCiAgICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LGZhY2UgPSAicGxhaW4iKSkgJiBOb0xlZ2VuZCgpICYgCiAgICAgICAgICBOb0F4ZXMoKQogICAgICBwbG90X2xzdFtbaV1dIDwtIGcKICAgIH0KICAgIAogICAgIyBDb21iaW5lIG11bHRpcGxlIHBsb3RzIGZvciBvdXRwdXQsIGFzIGRlc2lyZWQKICAgIHJldHVybihjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBwbG90X2xzdCwgbmNvbD01KSkKfQpgYGAKCmBgYHtyfQpwYXR0ZXJuX3Bsb3RfbGlzdCA8LSBsYXBwbHkoY2xvbmVfcGF0dGVybnMsIHBsb3RQYXR0ZXJuRHluQnlDbG9uZSkKCm5fcGxvdHNfcGVyX3BhdHRlcm4gPC0gc2FwcGx5KHBhdHRlcm5fcGxvdF9saXN0LCBmdW5jdGlvbih4KSBsZW5ndGgoeFtbJ2xheWVycyddXSkpCgojIyBjYWxjdWxhdGUgaGVpZ2h0IG9mIGZpZ3VyZSBiYXNlZCBvbiBudW1iZXIgb2YgcGxvdHMgcGVyIHBhdHRlcm4KcGx0X2hlaWdodCA8LSAyLjY2NyAqICgobl9wbG90c19wZXJfcGF0dGVybiAlLyUgNSkgKyAxKQpgYGAKCiMjIyMgRXh0IERhdGEgRmlnIDZiLGMKYGBge3IgZmlnLmhlaWdodD0xNiwgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbMV1dCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9NS4zMywgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbMl1dCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTEwLjY2NywgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbM11dCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTEzLjMzMywgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbNF1dCmBgYAoKYGBge3IgZmlnLmhlaWdodD0yLjY2NywgZmlnLndpZHRoPTEyfQpwYXR0ZXJuX3Bsb3RfbGlzdFtbNV1dCmBgYAoKYGBge3IgZmlnLmhlaWdodD01LjMzLCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1s2XV0KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTIuNjY3LCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1s3XV0KYGBgCiMjIFZpc3VhbGl6ZSBvbmx5IGNsb25lcyBtYXRjaGluZyBpbiB2aXZvIGluIEZBIHByb2plY3Rpb24KRXh0IERhdGEgRmlnLiA3ZgoKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9M30KSWRlbnRzKGNsb25lcykgPC0gIkNlbGxUYWdfQ2xvbmUiCiMgU3Vic2V0IGp1c3QgdGhlIGNsb25lcyB0aGF0IG1hdGNoIHRoZSBpbiB2aXRybywgcGF0dGVybiAxCmludml0cm9tYXRjaCA8LSBzdWJzZXQoY2xvbmVzLCBpZGVudHM9YygiUlBNX0Nsb25lXzE0IiwiUlBNX0Nsb25lXzIiLCJSUE1fQ2xvbmVfMzMiLCJSUE1fQ2xvbmVfMzYiLCJSUE1fQ2xvbmVfNiIpKQoKcDFfY2VsbHMgPC0gY29sbmFtZXMoaW52aXRyb21hdGNoKQpwMl9jZWxscyA8LSBjb2xuYW1lcyhzdWJzZXQoY2xvbmVzLCBpZGVudHM9IlJQTV9DbG9uZV8yMyIpKQpwNV9jZWxscyA8LSBjb2xuYW1lcyhzdWJzZXQoY2xvbmVzLCBpZGVudHM9IlJQTV9DbG9uZV8xMyIpKQoKRGltUGxvdChUQk9fc2V1cmF0LCBncm91cC5ieT0iQ2VsbFRhZ19DbG9uZSIsIHJlZHVjdGlvbj0nZmEnLCAKICAgICAgICBvcmRlcj1UUlVFLCBjZWxscy5oaWdobGlnaHQ9cDFfY2VsbHMsIHNpemVzLmhpZ2hsaWdodD0yLCAKICAgICAgICBjb2xzLmhpZ2hsaWdodD1jKCJvcmFuZ2UiKSkgKyBnZ3RpdGxlKCIiKSAmIE5vTGVnZW5kKCkgJiBOb0F4ZXMoKQpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSJDZWxsVGFnX0Nsb25lIiwgcmVkdWN0aW9uPSdmYScsIAogICAgICAgIG9yZGVyPVRSVUUsIGNlbGxzLmhpZ2hsaWdodD1wMl9jZWxscywgc2l6ZXMuaGlnaGxpZ2h0PTIsIAogICAgICAgIGNvbHMuaGlnaGxpZ2h0PXBhdHRlcm5fY29sb3JzWyJQYXR0ZXJuXzIiXSkgKyBnZ3RpdGxlKCIiKSAmIE5vTGVnZW5kKCkgJiBOb0F4ZXMoKQpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSJDZWxsVGFnX0Nsb25lIiwgcmVkdWN0aW9uPSdmYScsIAogICAgICAgIG9yZGVyPVRSVUUsIGNlbGxzLmhpZ2hsaWdodD1wNV9jZWxscywgc2l6ZXMuaGlnaGxpZ2h0PTIsIAogICAgICAgIGNvbHMuaGlnaGxpZ2h0PXBhdHRlcm5fY29sb3JzWyJQYXR0ZXJuXzUiXSkgKyBnZ3RpdGxlKCIiKSAmIE5vTGVnZW5kKCkgJiBOb0F4ZXMoKQoKCmBgYAoKCgojIyMjIEZpZy4gM2cgCmRwdCBwc3VlZG90aW1lIGluIEZBIHNwYWNlIApgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zLjc1LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpGZWF0dXJlUGxvdChUQk9fc2V1cmF0LCBmZWF0dXJlcyA9IGMoImRwdF9wc2V1ZG90aW1lIiksIHB0LnNpemU9MC4wMSwKICAgICAgICAgICAgcmVkdWN0aW9uPSdmYScsKSArIHNjYWxlX2NvbG9yX3ZpcmlkaXMob3B0aW9uPSJ2aXJpZGlzIixkaXJlY3Rpb249LTEpJiBOb0F4ZXMoKQoKYGBgCiMjIyMgRnVuY3Rpb24gdG8gcGxvdCBjbG9uZSBkeW5hbWljcyBjb2xvcmVkIGJ5IERQVApgYGB7cn0KcGxvdENsb25lRHluRHB0IDwtIGZ1bmN0aW9uKGNsb25lX3BhdHRlcm4sIHNldXJhdF9vYmosIHRpdGxlID0gIiIpIHsKICAgICMgU3Vic2V0IGNlbGxzIG9mIGludGVyZXN0CiAgICBzcyA8LSBzdWJzZXQoc2V1cmF0X29iaiwgaWRlbnRzID0gY2xvbmVfcGF0dGVybikKICAgIGhpZ2hsaWdodF9jZWxscyA8LSBjb2xuYW1lcyhzcykKCiAgICAjIEV4dHJhY3QgZW1iZWRkaW5ncyBhbmQgbWV0YWRhdGEKICAgIGZhX2Nvb3JkcyA8LSBFbWJlZGRpbmdzKHNldXJhdF9vYmosICJmYSIpCiAgICBkcHRfdmFscyA8LSBzZXVyYXRfb2JqQG1ldGEuZGF0YSRkcHRfcHNldWRvdGltZQogICAgbmFtZXMoZHB0X3ZhbHMpIDwtIHJvd25hbWVzKHNldXJhdF9vYmpAbWV0YS5kYXRhKQoKICAgIGRhdCA8LSBhcy5kYXRhLmZyYW1lKGZhX2Nvb3JkcykKICAgIGRhdCRoaWdobGlnaHQgPC0gaWZlbHNlKHJvd25hbWVzKGRhdCkgJWluJSBoaWdobGlnaHRfY2VsbHMsICJ5ZXMiLCAibm8iKQogICAgZGF0JHBzZXVkb3RpbWUgPC0gZHB0X3ZhbHNbcm93bmFtZXMoZGF0KV0KICAgIGNvbG5hbWVzKGRhdClbMToyXSA8LSBjKCJGQTEiLCAiRkEyIikgICMgbmFtZSBjb2x1bW5zIGZvciBjbGFyaXR5CgogICAgbGlicmFyeShnZ3Bsb3QyKQogICAgZ2dwbG90KGRhdCwgYWVzKHggPSBGQTEsIHkgPSBGQTIpKSArCiAgICAgICAgIyBCYWNrZ3JvdW5kIGNlbGxzCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRhdCwgaGlnaGxpZ2h0ID09ICJubyIpLCAKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdyZXk4NSIsIHNpemUgPSAwLjEpICsKICAgICAgICAjIEhpZ2hsaWdodGVkIGNlbGxzIGNvbG9yZWQgYnkgcHNldWRvdGltZQogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkYXQsIGhpZ2hsaWdodCA9PSAieWVzIiksIAogICAgICAgICAgICAgICAgICAgYWVzKGNvbG9yID0gcHNldWRvdGltZSksIHNpemUgPSAwLjI1KSArCiAgICAgICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJ0dXJibyIsIGRpcmVjdGlvbiA9IC0xKSArCiAgICAgICAgZ2d0aXRsZSh0aXRsZSkgKwogICAgICAgIHRoZW1lX3ZvaWQoKSArCiAgICAgICAgdGhlbWUoCiAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAgICAgICMgcmVtb3ZlcyBhbnkgcGFuZWwgYm9yZGVyCiAgICAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSBOQSksCiAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gTkEpLAogICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpCiAgICAgICAgKQp9CmBgYAoKCgojIyMjIEZpZyAzaApgYGB7ciBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD0xMiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KSWRlbnRzKGNsb25lcykgPC0gJ0Nsb25lX0R5bmFtaWNzJwoKbXlfcGF0cyA8LSBjKCdQYXR0ZXJuXzEnLCdQYXR0ZXJuXzInLCdQYXR0ZXJuXzUnLCdVbmtub3duXzEnLCdQYXR0ZXJuXzMnLCdQYXR0ZXJuXzQnKQoKcGxvdHMgPC0gbGFwcGx5KG15X3BhdHMsIGZ1bmN0aW9uKHBhdCkgewogICAgdGl0IDwtIGdzdWIocGF0LCAnXycsICcgJykKICAgIHBsb3RDbG9uZUR5bkRwdChwYXQsIGNsb25lcywgdGl0bGUgPSB0aXQpCn0pCgpjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBwbG90cywgbmNvbD02KQpgYGAKCg==